package org.byd.dmsja.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

public class SwdNode<T> {
    public SwdNode<T> swdParentNode = null;
    public List<SwdNode<T>> swdChildNodeList = new ArrayList<>();
    public T swdRecord = null;

    /*
     * swdForEachAncestor，向上遍历，不会遍历根节点及自己
     * */

    public void swdForEachAncestor(Consumer<SwdNode<T>> nodeConsumer, Boolean skipRootNodeIf) {
        AtomicReference<Consumer<SwdNode<T>>> acCallBack = new AtomicReference<>();
        acCallBack.set((SwdNode<T> xCurrentNode) -> {
            SwdNode<T> xParentNode = xCurrentNode.swdParentNode;
            if (xParentNode != null) {
                if (xParentNode.swdRecord == null) {
                    if (!skipRootNodeIf) {
                        nodeConsumer.accept(xParentNode);
                    }
                } else {
                    nodeConsumer.accept(xParentNode);
                }
                acCallBack.get().accept(xParentNode);
            }
        });
        acCallBack.get().accept(this);
    }

    public void swdForEachAncestor(Consumer<SwdNode<T>> nodeConsumer) {
        this.swdForEachAncestor(nodeConsumer, true);
    }


    public void swdForEachDescdant(Consumer<SwdNode<T>> nodeConsumer) {
        AtomicReference<Consumer<SwdNode<T>>> acCallBack = new AtomicReference<>();
        acCallBack.set((SwdNode<T> xCurrentNode) -> {
            List<SwdNode<T>> childNodeList = xCurrentNode.swdChildNodeList;
            childNodeList.forEach(x -> {
                nodeConsumer.accept(x);
                acCallBack.get().accept(x);
            });
        });
        acCallBack.get().accept(this);
    }

    public List<SwdNode<T>> swdFindDescdantNodeList(Function<T, Boolean> predicate) {
        List<SwdNode<T>> resultList = new ArrayList<>();
        this.swdForEachDescdant(x -> {
            Boolean isMatch = predicate.apply(x.swdRecord);
            if (isMatch) {
                resultList.add(x);
            }
        });
        return resultList;
    }

    public SwdNode<T> swdFindDescdantNodeFirst(Function<T, Boolean> predicate) {
        List<SwdNode<T>> descList = this.swdFindDescdantNodeList(predicate);
        return !descList.isEmpty() ? descList.get(0) : null;
    }

    public SwdNode<T> swdFindDescdantNodeLast(Function<T, Boolean> predicate) {
        List<SwdNode<T>> descList = this.swdFindDescdantNodeList(predicate);
        return !descList.isEmpty() ? descList.get(descList.size() - 1) : null;
    }

    public Integer swdGetDepth() {
        AtomicReference<Integer> depth = new AtomicReference<>(0);
        this.swdForEachAncestor(x -> {
            depth.getAndSet(depth.get() + 1);
        }, false);
        return depth.get();
    }

    public String swdGetPath(Function<T, Object> pathSelector, String deli) {
        List<Object> pathList = new ArrayList<>();
        this.swdForEachAncestor(x -> {
            pathList.add(pathSelector.apply(x.swdRecord));
        }, true);
        Collections.reverse(pathList);
        var result = pathList
                .stream()
                .map(x -> x == null ? "" : x.toString())
                .collect(Collectors.joining(deli));
        return result;
    }

    public List<SwdNode<T>> swdFindMatchedDescdantNodeList(Method.IPredicate1<T> predicate) {
        List<SwdNode<T>> resultList = new ArrayList<>();
        this.swdForEachDescdant(x -> {
            Boolean xIsMatch = predicate.apply(x.swdRecord);
            if (xIsMatch) {
                resultList.add(x);
            }
        });
        return resultList;
    }
}
